#pragma once

// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

// AnimationManager.h : interface of the AnimationManager class
//

#include "Node.h"
#include "Action.h"


struct AnimationInfo
{
    gx::Mat4 initialShape;
    gx::Mat4 intermediateShape;
    gx::Mat4 finalShape;
    ref<Action> action;
    double elapsedTime; // ms
    double totalTime; // ms
};


class AnimationManager
{
    // ************************************************************************
    // Singleton pattern
private:
    AnimationManager();
    AnimationManager(AnimationManager&); // undefined
    void operator=(AnimationManager&); // undefined
public:
    static AnimationManager & instance();
    // ************************************************************************

    // ************************************************************************
    typedef std::map<ref<Node>, AnimationInfo> BlockInfoMap;
    
    // If "iniShape" is NULL, we use the current shape at the specified  block 
    // "Action" might be NULL
    void startBlockAnimation(const Node & node, const gx::Mat4& iniShape, 
        const gx::Mat4& finalShape, double totalTime, ref<Action> action);
    
    const BlockInfoMap & getBlockInfoMap() const { return m_animatedBlocks; }
    bool isBlockAnimated(ref<Node> block) const;
    const AnimationInfo & getAnimationInfo(ref<Node> block) const;

    // Returns true if something's changed (lets us know we have to redraw)
    bool performAnimationStep(double elapsedMillis);

    // Helper functions that could belong in other place (e.g., ActionManager
    // or even BlockManager)
    // TODO the animation is lost on undo/redo. To fix that we would probably
    // need to create new animated action subclasses (i.e., AnimatedInsertBlockAction)
    void performAnimatedInsertBlockAction(ref<Node> block);
    void performAnimatedEraseBlockAction(const Node & block);

    // TODO add animated replace shape action

protected:
    // Used internally by performAnimationStep()
    void updateIntermediateShape(ref<Node> block, 
        const gx::Mat4& newIntermShape, double deltaTime);
    BlockInfoMap::const_iterator finalizeAnimation(ref<Node> block);

private:
    BlockInfoMap m_animatedBlocks;
    // ************************************************************************
};
